package com.yfqy.app.modules.order.serviceimpl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.yfqy.app.configure.Idgenerator.SnowflakeIdGenerator;
import com.yfqy.app.constant.GeneralConstants;
import com.yfqy.app.constant.RedisKeyConstants;
import com.yfqy.app.domain.entity.OrderInfo;
import com.yfqy.app.domain.entity.OrderRefund;
import com.yfqy.app.domain.mapper.OrderInfoMapper;
import com.yfqy.app.domain.mapper.OrderItemMapper;
import com.yfqy.app.domain.mapper.OrderRefundMapper;
import com.yfqy.app.domain.vo.ResultVO;
import com.yfqy.app.enums.ErrorCode;
import com.yfqy.app.enums.OrderItemStatus;
import com.yfqy.app.enums.OrderStatus;
import com.yfqy.app.enums.RefundReason;
import com.yfqy.app.enums.RefundStatus;
import com.yfqy.app.enums.RefundType;
import com.yfqy.app.exception.BusinessException;
import com.yfqy.app.service.UserService;
import com.yfqy.app.modules.order.dto.OrderExtendDTO;
import com.yfqy.app.modules.order.dto.OrderItemDTO;
import com.yfqy.app.modules.order.dto.RefundApplyRequest;
import com.yfqy.app.modules.order.service.OrderRefundService;
import com.yfqy.app.modules.wechat.service.WechatPayService;
import com.yfqy.app.security.login.LoginUserInfo;
import com.yfqy.app.util.AuthUserContext;
import com.yfqy.app.util.JSON;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderRefundServiceImpl extends UserService implements OrderRefundService {

    @Autowired
    private OrderRefundMapper orderRefundMapper;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Autowired
    @Qualifier("singleRedisClient")
    private RedissonClient redissonClient;

    @Autowired
    private SnowflakeIdGenerator idGenerator;

    @Autowired
    private WechatPayService wechatPayService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVO<Void> applyAfterSales(RefundApplyRequest request) {
        Long orderId = request.getOrderId();
        Long userId = getUserId();

        // 获取分布式锁，防止重复申请
        RLock lock = redissonClient.getLock(RedisKeyConstants.ORDER_REFUND_LOCK + orderId);
        try {
            boolean lockAcquired = lock.tryLock(5, 30, TimeUnit.SECONDS);
            if (!lockAcquired) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }

            // 1. 验证订单信息
            OrderInfo orderInfo = validateOrderForRefund(orderId, userId);

            // 2. 检查是否已有退款记录
            List<OrderRefund> orderRefunds = checkExistingRefund(orderId, request.getOrderItemIds());

            // 3. 验证退款商品
            List<OrderItemDTO> refundItems = validateRefundItems(orderId, request.getOrderItemIds());

            boolean isFullRefund = validateFullRefund(orderId, orderRefunds, refundItems);

            // 4. 计算退款金额
            BigDecimal refundAmount = calculateRefundAmount(refundItems, orderInfo);

            // 5. 创建退款记录
            createOrderRefund(request, orderInfo, refundAmount, isFullRefund);

            // 6. 更新订单状态
            updateOrderStatus(orderId, orderInfo.getOrderStatus(), OrderStatus.APPLY_REFUND.getStatus());

            // 7. 更新商品订单状态为退款中
            updateOrderItemStatus(request.getOrderItemIds(), OrderItemStatus.REFUNDING.getStatus());

            return ResultVO.success();

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVO<Void> cancelRefund(Long refundId) {
        try {
            Long userId = getUserId();

            // 1. 验证退款记录和权限
            OrderRefund orderRefund = validateRefundForCancellation(refundId, userId);

            // 获取分布式锁
            RLock lock = redissonClient.getLock(RedisKeyConstants.ORDER_REFUND_LOCK + refundId);
            try {
                boolean lockAcquired = lock.tryLock(5, 30, TimeUnit.SECONDS);
                if (!lockAcquired) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }

                // 2. 执行取消退款操作
                executeCancelRefund(orderRefund);

                return ResultVO.success();

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR.getCode(), "取消失败");
            } finally {
                releaseLock(lock);
            }
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("取消退款异常, refundId: {}", refundId, e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 验证退款记录和用户权限
     */
    private OrderRefund validateRefundForCancellation(Long refundId, Long userId) {
        OrderRefund orderRefund = orderRefundMapper.selectByPrimaryKey(refundId);
        if (orderRefund == null) {
            throw new BusinessException(ErrorCode.REFUND_NOT_EXIST);
        }

        // 验证退款状态：只能是申请中状态才能取消
        if (!RefundStatus.APPLYING.getStatus().equals(orderRefund.getRefundStatus())) {
            throw new BusinessException(ErrorCode.REFUND_STATUS_CANNOT_CANCEL);
        }

        // 验证用户权限：必须是退款申请本人才能取消
        if (!userId.equals(orderRefund.getUserId())) {
            throw new BusinessException(ErrorCode.REFUND_NOT_BELONG_TO_USER);
        }

        return orderRefund;
    }

    /**
     * 执行取消退款操作
     */
    private void executeCancelRefund(OrderRefund orderRefund) {
        // 1. 更新退款状态为已取消
        int updateCount = orderRefundMapper.updateOrderRefundStatus(
                orderRefund.getId(),
                RefundStatus.CANCELLED.getStatus(),
                orderRefund.getRefundStatus()
        );

        if (updateCount == 0) {
            throw new BusinessException(ErrorCode.REFUND_STATUS_CHANGED);
        }

        // 2. 恢复订单状态
        restoreOrderStatus(orderRefund.getOrderId());

        // 3. 恢复商品退款状态
        restoreOrderItemStatus(orderRefund);
    }

    /**
     * 恢复订单状态
     */
    private void restoreOrderStatus(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(orderId);
        OrderExtendDTO orderExtendDTO = JSON.parse(orderInfo.getExtend(), OrderExtendDTO.class);
        // 从扩展字段中获取订单状态，交换订单状态
        updateOrderStatus(orderId, orderInfo.getOrderStatus(), orderExtendDTO.getBeforeStatus());
    }

    /**
     * 恢复商品退款状态
     */
    private void restoreOrderItemStatus(OrderRefund orderRefund) {
        if (StringUtils.isNotBlank(orderRefund.getOrderItemId())) {
            try {
                List<Long> orderItemIds = Arrays.stream(orderRefund.getOrderItemId().split(","))
                        .map(String::trim)
                        .filter(StringUtils::isNotBlank)
                        .map(Long::valueOf)
                        .collect(Collectors.toList());

                if (!orderItemIds.isEmpty()) {
                    updateOrderItemStatus(orderItemIds, OrderItemStatus.NO_REFUND.getStatus());
                }
            } catch (NumberFormatException e) {
                log.error("解析订单商品ID失败, refundId: {}, orderItemIds: {}",
                        orderRefund.getId(), orderRefund.getOrderItemId(), e);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
        }
    }

    /**
     * 安全释放锁
     */
    private void releaseLock(RLock lock) {
        if (lock != null && lock.isHeldByCurrentThread()) {
            try {
                lock.unlock();
            } catch (IllegalMonitorStateException e) {
                log.warn("释放锁异常, 可能锁已自动释放", e);
            }
        }
    }

    /**
     * 验证订单是否可退款
     */
    private OrderInfo validateOrderForRefund(Long orderId, Long userId) {
        OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(orderId);
        if (orderInfo == null) {
            throw new BusinessException(ErrorCode.ORDER_NOT_EXIST);
        }

        // 验证订单所属用户
        if (!orderInfo.getUserId().equals(userId)) {
            log.warn("验证订单所属用户，不属于当前用户，userId:{},orderId:{}", userId, orderId);
            throw new BusinessException(ErrorCode.ORDER_NOT_EXIST);
        }

        // 验证订单状态是否可退款
        if (!canApplyRefund(orderInfo.getOrderStatus())) {
            throw new BusinessException(ErrorCode.ORDER_CANNOT_APPLY_REFUND);
        }

        return orderInfo;
    }

    /**
     * 检查是否已有退款记录
     */
    private List<OrderRefund> checkExistingRefund(Long orderId, List<Long> orderItemIds) {
        List<OrderRefund> existingRefunds = orderRefundMapper.selectByOrderId(orderId);
        if (!existingRefunds.isEmpty()) {
            // 检查是否有进行中的退款
            boolean hasProcessingRefund = existingRefunds.stream()
                    .anyMatch(refund -> {
                        // 解析逗号分隔的订单商品ID
                        Set<Long> refundItemIds = parseOrderItemIds(refund.getOrderItemId());

                        // 检查当前申请的订单商品ID是否在历史退款中
                        return orderItemIds.stream()
                                .anyMatch(refundItemIds::contains);
                    });

            if (hasProcessingRefund) {
                throw new BusinessException(ErrorCode.REFUND_ALREADY_EXISTS);
            }
        }
        return existingRefunds;
    }

    private Set<Long> parseOrderItemIds(String orderItemIdsStr) {
        try {
            return Arrays.stream(orderItemIdsStr.split(","))
                    .map(String::trim)
                    .filter(StringUtils::isNotBlank)
                    .map(Long::valueOf)
                    .collect(Collectors.toSet());
        } catch (NumberFormatException e) {
            log.warn("订单商品ID格式错误: {}", orderItemIdsStr, e);
            return Collections.emptySet();
        }
    }

    /**
     * 验证退款商品
     */
    private List<OrderItemDTO> validateRefundItems(Long orderId, List<Long> orderItemIds) {

        // 查询订单所有商品
        List<OrderItemDTO> allItems = orderItemMapper.selectOrderItemDTOsByOrderId(orderId);
        if (allItems.isEmpty()) {
            throw new BusinessException(ErrorCode.ORDER_ITEMS_NOT_FOUND);
        }

        // 验证商品是否属于该订单
        List<OrderItemDTO> refundItems = allItems.stream()
                .filter(item -> orderItemIds.contains(item.getId()))
                .collect(Collectors.toList());

        if (refundItems.size() != orderItemIds.size()) {
            throw new BusinessException(ErrorCode.REFUND_ITEMS_INVALID);
        }

        // 检查商品是否已退款
        for (OrderItemDTO item : refundItems) {
            if (item.getRefundStatus() != null && item.getRefundStatus() != 0) {
                throw new BusinessException(ErrorCode.ORDER_APPLY_REFUND);
            }
        }

        return refundItems;
    }

    private boolean validateFullRefund(Long orderId, List<OrderRefund> orderRefunds, List<OrderItemDTO> refundItems) {
        List<OrderItemDTO> allItems = orderItemMapper.selectOrderItemDTOsByOrderId(orderId);
        Set<Long> allOrderItemIds = allItems.stream().map(OrderItemDTO::getId).collect(Collectors.toSet());

        Set<Long> allRefundOrderItemIds = refundItems.stream().map(OrderItemDTO::getId).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(orderRefunds)) {
            allRefundOrderItemIds.addAll(
                    orderRefunds.stream()
                            .map(OrderRefund::getOrderItemId) // 获取逗号分隔的字符串
                            .map(this::parseOrderItemIds)     // 解析为Long集合
                            .flatMap(Set::stream)             // 扁平化为Long流
                            .collect(Collectors.toSet())      // 收集为Set
            );
        }

        return allOrderItemIds.equals(allRefundOrderItemIds);
    }

    /**
     * 计算退款金额
     */
    private BigDecimal calculateRefundAmount(List<OrderItemDTO> refundItems, OrderInfo orderInfo) {
        BigDecimal totalRefundAmount = refundItems.stream()
                .map(OrderItemDTO::getActualPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 如果是整单退款，加上运费
        if (refundItems.size() == getTotalOrderItems(orderInfo.getId())) {
            totalRefundAmount = totalRefundAmount.add(orderInfo.getFreightAmount());
        }

        return totalRefundAmount;
    }

    /**
     * 创建退款记录
     *
     * @param request      售后申请入参
     * @param orderInfo    主订单
     * @param refundAmount 退款订单金额
     * @param isFullRefund 是否整单退
     */
    private void createOrderRefund(RefundApplyRequest request, OrderInfo orderInfo,
                                   BigDecimal refundAmount, boolean isFullRefund) {
        OrderRefund orderRefund = new OrderRefund();
        orderRefund.setId(idGenerator.nextId());
        orderRefund.setUserId(orderInfo.getUserId());
        orderRefund.setOrderId(orderInfo.getId());
        orderRefund.setOrderItemId(request.getOrderItemIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
        orderRefund.setOrderNo(orderInfo.getOrderNo());
        orderRefund.setRefundNo(generateRefundNo());

        // 设置退款类型
        if (isFullRefund) {
            orderRefund.setRefundType(RefundType.FULL_ORDER.getType());
        } else {
            orderRefund.setRefundType(RefundType.PARTIAL_ITEMS.getType());
        }

        orderRefund.setRefundReason(request.getRefundReason());
        orderRefund.setRefundDesc(RefundReason.getReasonDesc(request.getRefundReason()));
        orderRefund.setApplyRefundAmount(refundAmount);
        orderRefund.setRefundStatus(RefundStatus.APPLYING.getStatus());
        orderRefund.setPaymentType(orderInfo.getPaymentType());
        orderRefund.setImages(request.getImages() != null ? String.join(",", request.getImages()) : null);
        orderRefund.setRemark(request.getRemark());

        orderRefundMapper.insert(orderRefund);
    }

    /**
     * 更新主订单状态
     *
     * @param orderId     订单主键
     * @param currStatus  更新前的状态即当前状态
     * @param orderStatus 需要更新到什么状态
     */
    private void updateOrderStatus(Long orderId, byte currStatus, byte orderStatus) {
        OrderInfo updateOrder = new OrderInfo();
        updateOrder.setId(orderId);
        updateOrder.setOrderStatus(orderStatus);

        OrderExtendDTO orderExtendDTO = new OrderExtendDTO();
        orderExtendDTO.setBeforeStatus(currStatus);
        updateOrder.setExtend(JSON.stringify(orderExtendDTO));

        int updated = orderInfoMapper.updateOrderStatus(updateOrder, currStatus);
        if (updated == 0) {
            throw new BusinessException(ErrorCode.ORDER_STATUS_CHANGED);
        }
    }

    /**
     * 更新订单商品表状态
     *
     * @param refundOrderItemIds
     */
    private void updateOrderItemStatus(List<Long> refundOrderItemIds, byte orderItemStatus) {
        orderItemMapper.updateOrderItemStatus(refundOrderItemIds, orderItemStatus);
    }

    /**
     * 生成退款单号
     */
    private String generateRefundNo() {
        return "R" + System.currentTimeMillis() + RandomUtil.randomNumbers(4);
    }

    /**
     * 获取订单商品总数
     */
    private int getTotalOrderItems(Long orderId) {
        return orderItemMapper.countByOrderId(orderId);
    }

    /**
     * 检查订单状态是否可退款，主订单申请退款不能再次申请
     */
    private boolean canApplyRefund(Byte orderStatus) {
        return Objects.equals(orderStatus, OrderStatus.COMPLETED.getStatus()) ||
                Objects.equals(orderStatus, OrderStatus.PART_REFUNDED.getStatus()) ||
                Objects.equals(orderStatus, OrderStatus.SHIPPED.getStatus()) ||
                Objects.equals(orderStatus, OrderStatus.OUTBOUND.getStatus());
    }

    private LoginUserInfo validateUserLogin() {
        LoginUserInfo loginUserInfo = AuthUserContext.getLocalAuthUser();
        if (loginUserInfo == null || Objects.equals(GeneralConstants.DEFAULT_USER_ID, loginUserInfo.getId())) {
            throw new BusinessException(ErrorCode.USER_NOT_LOGIN);
        }
        return loginUserInfo;
    }

}